from django import forms
from web import models
from django.core.validators import RegexValidator
from django.core.exceptions import ValidationError
from django.conf import settings
import random
from utils.tencent.sms import send_sms_single
from django_redis import get_redis_connection
from utils import encrypt
from web.forms.bootstrap import BootStrapForm

# 跟数据库有关，继承 forms.ModelForm
class RegisterModelForm(forms.ModelForm):
    mobile_phone = forms.CharField(
        label='手机号',
        validators=[RegexValidator(r'^(1[3|4|5|6|7|8|9])\d{9}$', '手机号格式错误')]
    )
    # password = forms.CharField(label="密码", widget=forms.PasswordInput(attrs={'class','form-control', 'placeholder': '请输入密码'}))
    password = forms.CharField(
        label="密码",
        min_length=8,
        max_length=16,
        error_messages={
            'min_length': '密码长度不能小于8位',
            'max_length': '密码长度不能大于16位'
        },
        widget=forms.PasswordInput()
    )

    confirm_password = forms.CharField(
        label="重复密码",
        widget=forms.PasswordInput()
    )

    code = forms.CharField(
        label="验证码",
        widget=forms.TextInput()
    )

    class Meta:
        model = models.UserInfo
        fields = ['username', 'email', 'password', 'confirm_password', 'mobile_phone', 'code']

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        for name, field in self.fields.items():
            field.widget.attrs['class'] = 'form-control'
            field.widget.attrs['placeholder'] = '请输入{}'.format(field.label)

    # 用户名校验钩子 --> 用户名不能重复
    def clean_username(self):
        username = self.cleaned_data['username']
        exists = models.UserInfo.objects.filter(username=username).exists()
        if exists:
            raise ValidationError('用户名已存在')
        return username

    # 邮箱校验钩子 --> 邮箱不能重复
    def clean_email(self):
        email = self.cleaned_data['email']
        exists = models.UserInfo.objects.filter(email=email).exists()
        if exists:
            raise ValidationError('邮箱已存在')
        return email

    # 密码校验钩子 --> 加密 & 返回
    def clean_password(self):
        pwd = self.cleaned_data['password']
        # 加密 & 返回
        return encrypt.md5(pwd)

    # 判断重复密码的钩子 --> 重复密码必须与密码相等
    def clean_confirm_password(self):
        pwd = self.cleaned_data.get('password')
        confirm_pwd = encrypt.md5(self.cleaned_data['confirm_password'])
        if pwd != confirm_pwd:
            raise ValidationError('两次密码不一致')

        return confirm_pwd

    # 判断重复密码的钩子 --> 重复密码必须与密码相等
    def clean_mobile_phone(self):
        mobile_phone = self.cleaned_data['mobile_phone']
        exists = models.UserInfo.objects.filter(mobile_phone=mobile_phone).exists()
        if exists:
            raise ValidationError('手机号已注册过，请选择其他手机号注册')
        return mobile_phone

    # 判断手机号和验证码的钩子 --> 手机号必须与发送验证码是一致，验证码必须是短信发送的验证码
    def clean_code(self):
        code = self.cleaned_data['code']
        mobile_phone = self.cleaned_data.get('mobile_phone')
        if not mobile_phone:
            return code

        conn = get_redis_connection()
        redis_code = conn.get(mobile_phone)
        if not redis_code:
            raise ValidationError('验证码失效或未发送，请重新发送')

        redis_str_code = redis_code.decode('utf-8')
        if code.strip() != redis_str_code:
            raise ValidationError('验证码错误，请重新输入')
        return code

# 跟数据库无关，继承 forms.Form
class SendSMSForm(forms.Form):
    mobile_phone = forms.CharField(label='手机号', validators=[RegexValidator(r'^(1[3|4|5|6|7|8|9])\d{9}$', '手机号格式错误')])

    def __init__(self, request, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.request = request

    def clean_mobile_phone(self):
        """ 手机号校验的钩子 """
        # 用户提交的手机号
        mobile_phone = self.cleaned_data["mobile_phone"]

        # 判断短信模板是否有问题
        tpl = self.request.GET.get('tpl')
        tpl_id = settings.TENCENT_SMS_TEMPLATE.get(tpl)
        if not tpl_id:
            raise ValidationError("短信模板错误")
        # 校验数据库中是否已经有手机号
        exists = models.UserInfo.objects.filter(mobile_phone=mobile_phone).exists()
        if tpl == 'login':
            if not exists:
                raise ValidationError('手机号不存在')
        else:

            if exists:
                raise ValidationError('手机号已存在')

        # 发短信 & 写入redis
        code = random.randrange(1000, 9999)
        print("验证码：{}".format(code))
        print("使用短信功能时,将web/forms/account.py下139-141行的注释取消")
        # 使用短信功能时，将以下三行的注释取消
        sms = send_sms_single(mobile_phone, tpl_id, [code,1])
        if sms['result'] != 0:
            raise ValidationError("短信发送失败，{}".format(sms['errmsg']))

        # 验证码 写入 redis (django_redis)
        conn = get_redis_connection()
        # 60秒有效时间
        conn.set(mobile_phone, code, ex=60)
        return mobile_phone


# 短信登录相关Form
class LoginSMSForm(BootStrapForm, forms.Form):
    mobile_phone = forms.CharField(
        label='手机号',
        validators=[RegexValidator(r'^(1[3|4|5|6|7|8|9])\d{9}$', '手机号格式错误')]
    )

    code = forms.CharField(
        label="验证码",
        widget=forms.TextInput()
    )

    # def __init__(self, *args, **kwargs):
    #     super().__init__(*args, **kwargs)
    #     for name, field in self.fields.items():
    #         field.widget.attrs['class'] = 'form-control'
    #         field.widget.attrs['placeholder'] = '请输入{}'.format(field.label)

    # 校验手机号的钩子 --> 数据库中是否存在该手机号
    def clean_mobile_phone(self):
        mobile_phone = self.cleaned_data.get("mobile_phone")
        user_object = models.UserInfo.objects.filter(mobile_phone=mobile_phone).first()
        if not user_object:
            raise ValidationError('手机号不存在')
        return user_object

    # 校验验证码的钩子 -->
    def clean_code(self):
        code = self.cleaned_data.get("code")
        mobile_phone = self.cleaned_data.get("mobile_phone").mobile_phone

        # 手机号不存在，则验证码无需校验
        if not mobile_phone:
            return code

        conn = get_redis_connection()
        redis_code = conn.get(mobile_phone)
        if not redis_code:
            raise ValidationError('验证码失效或未发送，请重新发送')

        redis_str_code = redis_code.decode('utf-8')
        if code.strip() != redis_str_code:
            raise ValidationError('验证码错误，请重新输入')
        return code

# 账号密码登录相关Form
class LoginForm(BootStrapForm, forms.Form):
    username = forms.CharField(label='邮箱或手机号', max_length=32)
    password = forms.CharField(
        label='密码',
        min_length=8,
        max_length=16,
        error_messages={
            'min_length': '密码长度不能小于8位',
            'max_length': '密码长度不能大于16位'
        },
        widget=forms.PasswordInput(render_value=True))  # render_value=True, 页面刷新时，密码不丢失
    code = forms.CharField(label='图片验证码')

    def __init__(self, request, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.request = request

    # 校验密码的钩子 --> 密码 md5 加密
    def clean_password(self):
        pwd = self.cleaned_data['password']
        # 加密 &　返回
        return encrypt.md5(pwd)

    # 图片验证码的钩子 --> 验证码是否正确
    def clean_code(self):
        # 读取用户输入的图片验证码
        code = self.cleaned_data['code']
        # 读取session中自己的验证码
        session_code = self.request.session.get('image_code')
        if not session_code:
            raise ValidationError('验证码已过期，请重新获取')

        if code.strip().upper() != session_code.upper():
            raise ValidationError('验证码输入错误')

        return code